﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using RLProject1.Model;

namespace RLProject1.ViewModel {
    /// <summary>
    /// Vereint alle Q-Werte zu einer Grid-World.
    /// <para>
    /// Zu jedem <see cref="GridStateQValues"/> wird ein <see cref="GridFieldViewModel"/> erzeugt.
    /// Diese sind zeilenweise in der Eigenschaft QValuesRows festgehalten.
    /// </para>
    /// </summary>
    class GridViewModel : ViewModelBase {

        #region Properties

        public ObservableCollection<GridFieldViewModelRow> QValuesRows {
            get;
            private set;
        }

        /// <summary>Anzahl Reihen</summary>
        public int RowCount { get; private set; }

        /// <summary>Anzahl Spalten</summary>
        public int ColumnCount { get; private set; }

        #endregion

        
        /// <summary>
        /// Neues Datenmodell für eine GridView.
        /// </summary>
        /// <param name="values">Liste der Q-Werte</param>
        /// <param name="rowCount">Anzahl Zeilen (der GridWorld)</param>
        /// <param name="eTraces">optionale Referez auf eligibility-Werte</param>
        public GridViewModel(SarsaForDummies agent, int rowCount) {
            if (agent == null)
                throw new ArgumentNullException("agent");
            this.QValuesRows = new ObservableCollection<GridFieldViewModelRow>();
            RowCount = rowCount;
            ColumnCount = agent.QFunction.Count / rowCount;
            // Erstelle Reihenweise GridFieldViewModels für einzelne Felder:
            for (int r = 0; r < rowCount; r++) {
                var row = new ObservableCollection<GridFieldViewModel>();
                for (int c = 0; c < ColumnCount; c++) {
                    int idx = r * ColumnCount + c;
                    if (idx >= agent.QFunction.Count)
                        break;
                    GridStateQValues qVals = agent.QFunction[idx];
                    row.Add(new GridFieldViewModel(agent, qVals));
                }
                QValuesRows.Add(new GridFieldViewModelRow(row));
            }
        }

        internal void hideArrows() {
            foreach (GridFieldViewModelRow gfvmRow in QValuesRows)
                gfvmRow.hideArrows();
        }

        //internal void setEligibilities(double[][] eTraces) {
        //    foreach (GridFieldViewModelRow gfvmRow in QValuesRows)
        //        gfvmRow.setEligibilities(eTraces);
        //}

        /// <summary>
        /// Setzt den 'Besetzt'-Status eines Feldes (<see cref="GridFieldViewModel.Occupied"/>).
        /// </summary>
        /// <param name="row">Zeilennummer des Feldes</param>
        /// <param name="col">Spaltennummer des Feldes</param>
        /// <param name="isOccupied">True, für 'Feld ist besetzt'. False für 'Feld ist frei'.</param>
        internal void setOccupied(int row, int col, bool isOccupied) {
            try {
                QValuesRows[row].setOccupied(col, isOccupied);
            }
            catch (ArgumentOutOfRangeException) {
                Debug.WriteLine("GridViewModelRow.setOccupied(" + row + "," + isOccupied + ")\n\t" + 
                    row + " ist keine passende Zeile.", "Semantischer Laufzeitfehler");
            }
        }



        /// <summary>
        /// Hilfsklasse zur Darstellung einer Grid-Reihe.
        /// </summary>
        public class GridFieldViewModelRow {
            /// <summary>
            /// Gibt eine Liste der Felder dieser Reihe.
            /// </summary>
            public ObservableCollection<GridFieldViewModel> GFVM_Row { get; private set; }
            public GridFieldViewModelRow(ObservableCollection<GridFieldViewModel> row) {
                if (row == null)
                    throw new ArgumentNullException("row collection is null");
                GFVM_Row = row;
            }

            internal void hideArrows() {
                foreach (GridFieldViewModel gfvm in GFVM_Row)
                    gfvm.ShowArrow = false;
            }

            //internal void setEligibilities(double[][] eTraces) {
            //    foreach (GridFieldViewModel gfvm in GFVM_Row)
            //        gfvm.setETraces(eTraces);
            //}

            /// <summary>
            /// Setzt den 'Besetzt'-Status eines Feldes (<see cref="GridFieldViewModel.Occupied"/>).
            /// </summary>
            /// <param name="col">Spaltennummer des Feldes</param>
            /// <param name="isOccupied">True, für 'Feld ist besetzt'. False für 'Feld ist frei'.</param>
            internal void setOccupied(int col, bool isOccupied) {
                try {
                    GFVM_Row[col].Occupied = isOccupied;
                }
                catch (ArgumentOutOfRangeException) {
                    Debug.WriteLine("GridFieldViewModelRow.setOccupied(" + col + ","+isOccupied+")\n\t" + 
                    col + " ist keine passende Spalte", "Semantischer Laufzeitfehler");
                }
            }
        }
    }
}
